//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include <rs/Atomic.hpp>
#include <rs/Thread.hpp>
#include <rs/unittest/UnitTest.hpp>
#include <limits>

namespace
{
    using namespace rs;
    using namespace rs::unittest;

	template< typename T >
	void TestForType() {
		T const kLow = std::numeric_limits< T >::min() + 1;
		T const kHigh = std::numeric_limits< T >::max() - 1;
		
		// Test AddFetch
		{
			T x = kLow;
			T increment = kHigh;
			T expected = x + increment;
			
			RS_ASSERT(atomic::AddFetch(&x, increment) == expected, "atomic::AddFetch failed");
			RS_ASSERT(x == expected, "x has incorrect value after AddFetch");
		}
		
		// Test Exchange
		{
			T x = kLow;
			T y = kHigh;
						
			RS_ASSERT(atomic::Exchange(&x, y) == kLow, "atomic::Exchange failed");
			RS_ASSERT(x == kHigh, "x has incorrect value after Exchange");
		}
		
		// Test CompareSwap
		{
			T x = kLow;
			
			RS_ASSERT(atomic::CompareSwap(&x, kHigh, kLow) == false, "atomic::CompareSwap succeded but should of failed");
			RS_ASSERT(x == kLow, "'x' should still equal 'kLow' in atomic::CompareSwap test");
			
			RS_ASSERT(atomic::CompareSwap(&x, kLow, kHigh) == true, "atomic::CompareSwap failed");
			RS_ASSERT(x == kHigh, "'x' does not equal 'kHigh' in atomic::CompareSwap test");
		}
	}

    RS_TEST_CASE(rs::Atomic, AtomicUInt8Tests)
    {
		TestForType< UInt8 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicInt8Tests)
    {
		TestForType< Int8 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicUInt16Tests)
    {
		TestForType< UInt16 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicInt16Tests)
    {
		TestForType< Int16 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicUInt32Tests)
    {
		TestForType< UInt32 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicInt32Tests)
    {
		TestForType< Int32 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicUInt64Tests)
    {
		TestForType< UInt64 >();
    }

	RS_TEST_CASE(rs::Atomic, AtomicInt64Tests)
    {
		TestForType< Int64 >();
    }
    

	Atomic< Boolean > ready(false);
	Atomic< int > data(0);
	
	void AtomicVariableThread1(void*) {
		data.Store(1, MemoryOrder::Release);
		ready.Store(true, MemoryOrder::Release);
	}
	
	void AtomicVariableThread2(void*) {
		if (ready.Load(MemoryOrder::Acquire)) {
			RS_ASSERT(data.Load(MemoryOrder::Acquire) == 1, "data did not equal 1 when should of.");
		}
	}

	RS_TEST_CASE(rs::Atomic, AtomicVariable)
	{
		enum { kNumTrials = 20 };
		for (int i = 0; i < kNumTrials; ++i) {
			//Thread thread1(AtomicVariableThread1);
			Thread thread2(AtomicVariableThread2);
			AtomicVariableThread1(0);
			thread2.Start();
			thread2.Join();
			ready.Store(false);
			data.Store(0);
		}
	}
}